# USP Tracker plugin
##Description:Tracker Search plugin by Quikee
import gtk
import gtk.glade
import sys
import os
import gobject
import datetime
import gconf
import fcntl
import pango
import dbus
import gnomevfs
import gnome.ui

try:
	import beagle
except:
	print "Beagle not found.."

from execute import Execute
from easygconf import *
from easyfiles import *
from easybuttons import *
from usp_util import *

class Tracker:
	def __init__(self):
		self.tracker = None
		self.services = ['Files','Documents','Images','Music','Text Files','Videos']

	def ensureInitialization(self):
		if self.tracker == None:
			print "Initializing Tracker..."
			try:
				bus = dbus.SessionBus()
				self.tracker = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker')
				self.search_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Search')
				print "Initialization Complete..."				
			except:
				self.tracker = None
				print "Initialization Failed..."				
		return self

	def search(self, text, service='Files', offset=0, max_hits=-1):
		if self.tracker == None:
			return None
		returnedfiles = self.search_iface.TextDetailed(1, service, text, offset, max_hits)
		if len(returnedfiles) > 0:
			return returnedfiles
		else:
			return None
	
class Beagle:
	def hits_added_cb (self, query, response):
		hits = response.get_hits()

		for hit in hits:
			hitType = hit.get_type()
			if hitType == "File":
					hitUri = hit.get_parent_uri()
					if hitUri == None:
						hitUri = hit.get_uri()						
						hitFilePath = gnomevfs.get_local_path_from_uri(hitUri)
						hitMime = hit.get_mime_type()
						self.results.append([hitFilePath, hitType, hitMime])

	def finished_cb (self, query, response, loop):
		self.main_loop.quit()

	def search(self, queryString):
		self.results = []
		
		client = beagle.Client()
		self.main_loop = gobject.MainLoop()
		query = beagle.Query()
		query.add_text(queryString)
		query.connect("hits-added", self.hits_added_cb)
		query.connect("finished", self.finished_cb, self.main_loop)
		client.send_request_async(query)
		self.main_loop.run()
		return self.results

class pluginclass:
	"""This is the main class for the plugin"""
	"""It MUST be named pluginclass"""

	def __init__( self, USPWin ):

		self.USPWin = USPWin
		
		#The Glade file for the plugin
		self.gladefile = os.path.join( os.path.dirname( __file__ ), "tracker.glade" )

		#Read GLADE file
		self.wTree = gtk.glade.XML( self.gladefile, 'window1' )

		self.RecentTips = gtk.Tooltips()

		#These properties are NECESSARY to maintain consistency
		#throughout USP

		#Set 'window' property for the plugin (Must be the root widget)
		self.window = self.wTree.get_widget( "window1" )

		#Set 'heading' property for plugin
		self.heading = "Search"

		#This should be the first item added to the window in glade
		self.content_holder = self.wTree.get_widget( "eventbox1" )

		# Items to get custom colors
		self.itemstocolor = [self.wTree.get_widget( "viewport2" )]

		#Specify plugin width
		self.width = 180

		#Plugin icon
		self.icon = 'gnome-folder.png'

		self.tracker = Tracker()
		#self.beagle = Beagle()
		self.searchText = None
		self.thumb_factory = gnome.ui.ThumbnailFactory("normal")
		self.filterUsed = self.tracker.services[0]

		self.gconf_dir = '/apps/usp/plugins/tracker'
		self.client = gconf.client_get_default()
		self.client.add_dir( self.gconf_dir, gconf.CLIENT_PRELOAD_NONE )
		self.client.notify_add( self.gconf_dir + '/height', self.RegenPlugin )
		self.client.notify_add( self.gconf_dir + '/width', self.RegenPlugin )
		self.client.notify_add( self.gconf_dir + '/font_size', self.RegenPlugin )
		self.client.notify_add( self.gconf_dir + '/max_number_of_search_result', self.RegenPlugin )

		self.RegenPlugin()
	
		self.wTree.get_widget( "SearchTab" ).set_current_page(1)

		#Connect event handlers
		dic = { "on_clearButton_clicked" : self.clearSearchBox, "on_searchEntry_changed" : self.searchEntryChanged }
		
		self.wTree.signal_autoconnect(dic)
		

	def RegenPlugin( self, *args, **kargs ):
		self.GetGconfEntries()

	def GetGconfEntries( self ):
		self.client = gconf.client_get_default()
		self.height = SetGconf( self.client, 'int', self.gconf_dir+'/height', 385 )
		self.width = SetGconf( self.client, 'int', self.gconf_dir+'/width', 250 )
		self.fontsize = SetGconf( self.client, 'int', self.gconf_dir+'/font_size', 10 )
		
		# Hide vertical dotted separator
		self.hideseparator = SetGconf( self.client, "bool", self.gconf_dir+"/hide_separator", False )
		
		# Plugin icon
		self.icon = SetGconf( self.client, "string", self.gconf_dir+'/icon', "system-search" )
		
		# Allow plugin to be minimized to the left plugin pane
		self.sticky = SetGconf( self.client, "bool", self.gconf_dir+"/sticky", False )
		self.minimized = SetGconf( self.client, "bool", self.gconf_dir+"/minimized", False )

		# Maximum number of results tracker should return
		self.maximumNumberOfResults = SetGconf( self.client, "int", self.gconf_dir+"/max_number_of_search_result", 10 )

		self.RebuildPlugin()

	def SetHidden( self, state ):
		result = (state == True)
		WriteGconf(self.client, "bool", self.gconf_dir+"/minimized", result)

	def RebuildPlugin(self):
		self.content_holder.set_size_request(self.width, self.height)
		self.searchText = None
		self.updateFileList()
		self.updateMenuList()

	def updateMenuList(self):
		menuBox = self.wTree.get_widget( "MenuBox" )

		if len(menuBox.get_children()) > 0:
			for each in menuBox.get_children():
				each.destroy()
				
		for eachService in self.tracker.services:
			self.addButtonToMenuBox(eachService)

		
	def addButtonToMenuBox(self, name):
		menuBox = self.wTree.get_widget( "MenuBox" )
		
		button = gtk.Button( "", "ok", True )
		button.remove( button.get_children()[0] )
		button.set_size_request( 130-10, -1 )
		button.set_relief( gtk.RELIEF_NONE )
		button.connect( "clicked", self.changeFilter, name )
		button.set_focus_on_click(False)


		align = gtk.Alignment( 0, 0.5, 0, 0 )
		align.set_padding( 0, 0, 0, 0 )
		hBox = gtk.HBox( False, 5 )
		vBox = gtk.VBox( False, 2 )

		vBox.show()

		label = gtk.Label( name )
		label.set_size_request( button.size_request()[0]-10, -1 )
		label.set_ellipsize( pango.ELLIPSIZE_END )
		label.show()
		vBox.add( label )

		hBox.add( vBox )
		hBox.show()
		align.add( hBox )
		align.show()
		button.add( align )
		button.show()

		menuBox.pack_start( button, False, True, 0 )

	def updateFileList( self, *args, **kargs ):
	
		# remove file box entries
		fileBox = self.wTree.get_widget( "SearchBox" )
		for each in fileBox.get_children():
			each.destroy()
			#each.remove()
			
		self.wTree.get_widget( "vbox1" ).set_size_request( self.width, self.height )
		
		# search box is empty		
		if self.searchText == None or self.searchText == '':
			return True

		# search for files..
		fileList = self.tracker.ensureInitialization().search(self.searchText, self.filterUsed, 0 ,self.maximumNumberOfResults)
#		fileList = self.beagle.search(self.searchText)
		if fileList == None:
			return True
		
		#update search box entries		
		for eachFile in fileList:
			self.addButtonToSearchBox( eachFile[0], eachFile[1], eachFile[2])
		return True

	def clearSearchBox(self, *args, **kargs):
		self.searchText = ''
		self.wTree.get_widget("searchEntry").set_text(self.searchText)
		self.updateFileList()
		self.updateMenuList()

	def addButtonToSearchBox( self, filePath, fileType, fileMime ):
		icon = gnome.ui.icon_lookup(icon_theme, self.thumb_factory, filePath, fileMime, 0)[0]
		
		fileNameOnly=os.path.basename(filePath)
		
		button = gtk.Button( "", "ok", True )
		button.remove( button.get_children()[0] )
		button.set_size_request( self.width-150-24, -1 )
		button.set_relief( gtk.RELIEF_NONE )
		button.connect( "button_press_event", self.buttonClicked, filePath, fileMime )

		align = gtk.Alignment( 0, 0.5, 0, 0 )
		align.set_padding( 0, 0, 0, 0 )
		hBox = gtk.HBox( False, 5 )
		vBox = gtk.VBox( False, 2 )

		vBox.show()

		label = gtk.Label( fileNameOnly )
		label.set_size_request( button.size_request()[0]-20, -1 )
		label.set_ellipsize( pango.ELLIPSIZE_END )
		label.show()
		vBox.add( label )

		buttonIcon = gtk.Image()
		buttonIcon.set_from_pixbuf(icon_factory.load_icon(icon, gtk.ICON_SIZE_MENU, False))
		hBox.add( buttonIcon )

		buttonIcon.show()

		hBox.add( vBox )
		hBox.show()
		align.add( hBox )
		align.show()
		button.add( align )
		button.show()
		self.RecentTips.set_tip( button, filePath, tip_private=None )

		self.wTree.get_widget( "SearchBox" ).pack_start( button, False, True, 0 )

	def buttonClicked(self, widget, event, fileName, fileMime):
		if event.button == 1:
			command = gnomevfs.mime_get_default_application(fileMime)[2].split()
			command.append(fileName)
			self.executeCommand(command)
		elif event.button == 3:
			self.showMenuFor(widget, event, fileName, fileMime)

	def executeCommand(self, command):
		self.USPWin.wTree.get_widget( "window1" ).hide()
		
		os.chdir(os.path.expanduser("~"))
		pid = os.fork()
		if pid:
			os.spawnvp(os.P_NOWAIT,command[0], command)
			os._exit(0)

	def showMenuFor(self, itemClicked, ev, fileName, fileMime):
		self.mTree = gtk.glade.XML( self.gladefile, "menu1" )
		menu = self.mTree.get_widget("menu1")		
		x = int( ev.x )
		y = int( ev.y )
		time = ev.time
				
		for eachApplication in gnomevfs.mime_get_all_applications(fileMime):
			command = eachApplication[2].split()
			command.append(fileName)
			item = gtk.MenuItem(eachApplication[1], True)
			item.show()
			item.connect( "activate", self.menuItemClicked, command)
			menu.add(item)
		menu.popup( None, None, None, ev.button, time )

	def menuItemClicked(self, widget, command):
		self.executeCommand(command)

	def changeFilter(self, widget, filterName):
		self.filterUsed = filterName
		for each in self.wTree.get_widget("MenuBox").get_children():
			each.set_relief( gtk.RELIEF_NONE )
		widget.set_relief( gtk.RELIEF_HALF )
		self.updateFileList()
		
	def searchEntryChanged(self, *args, **kargs):
		self.wTree.get_widget("searchEntry").grab_focus()
		self.searchText = self.wTree.get_widget("searchEntry").get_text()
		self.updateFileList()
		
	def do_plugin(self):
		self.searchText = None
		self.updateFileList()
		self.updateMenuList()


#---------------------------------------------------------------------------------------#
# USPconfig Section Below
#---------------------------------------------------------------------------------------#
# This is the Config Class it must be called "cfgpluginclass'
class cfgpluginclass:
	def __init__(self):
		# The Gladefile for the plugins USPconfig Tab
		self.gladefile = os.path.join(os.path.dirname(__file__), "tracker.glade")
		# Read GLADE file
		self.wTree = gtk.glade.XML(self.gladefile,"config")
		# Set 'window' property for the plugin (Must be the root widget)
		self.window = self.wTree.get_widget("config")
		# Content Place Holder must be eventbox1
		self.content_holder = self.wTree.get_widget("eventbox2")
		# Set Heading, this will be used for the tab label in USPconfig
		self.heading = "Tracker"
		# GConf Stuff - This just makes sure a gconf path is there.
		self.gconf_dir = '/apps/usp/plugins/tracker'
		self.client = gconf.client_get_default()
		self.client.add_dir(self.gconf_dir, gconf.CLIENT_PRELOAD_NONE)
		# Setup the Functions of the Glade files spin controls or entry boxes for Value Changes
		# Tip: For spinbutton controls use the "_value_changed" event not the "_changed" event.
		dic = { "on_window1_destroy" : gtk.main_quit,
			"on_TrackerFontSizeSpin_changed" : self.fontspin,
			"on_TrackerHSpin_changed" : self.hspin,
			"on_TrackerWSpin_changed" : self.wspin,
			"on_TrackerIconEntry_changed" : self.iconchange,
			"on_StickyChkBtn_toggled" : self.setsticky,
			"on_MaximumNumberOfResultSpin1_value_changed" : self.maxNumberOfResults }
		self.wTree.signal_autoconnect(dic)

		# Read Values from GConf for USPconfig Tab and Set Default Values in GConf if they don't exist.
		self.wTree.get_widget("FontSizeSpin").set_value(SetGconf(self.client,'int', self.gconf_dir+'/font_size',9))
		self.wTree.get_widget("HSpin").set_value(SetGconf(self.client,'int', self.gconf_dir+'/height',200))
		self.wTree.get_widget("WSpin").set_value(SetGconf(self.client,'int', self.gconf_dir+'/width',250))
		self.wTree.get_widget("IconEntry").set_text(SetGconf(self.client,'string',self.gconf_dir+'/icon','system-search'))
		self.wTree.get_widget("StickyChkBtn").set_active(SetGconf(self.client,'bool',self.gconf_dir+'/sticky',False))
		self.wTree.get_widget("MaximumNumberOfResultSpin1").set_value(SetGconf(self.client,'int', self.gconf_dir+'/max_number_of_search_result',10))

	# Functions for Changing Values when items checked or altered Section
	def fontspin(self, widget, **kargs):
		self.client.set_int(self.gconf_dir+'/font_size',int(self.wTree.get_widget("FontSizeSpin").get_value()))

	def hspin(self, widget, **kargs):
		self.client.set_int(self.gconf_dir+'/height',int(self.wTree.get_widget("HSpin").get_value()))

	def wspin(self, widget, **kargs):
		self.client.set_int(self.gconf_dir+'/width',int(self.wTree.get_widget("WSpin").get_value()))

	def iconchange(self, widget, **kargs): 
		self.icon=self.wTree.get_widget("IconEntry").get_text()
		self.client.set_string(self.gconf_dir+'/icon',self.wTree.get_widget('IconEntry').get_text())

	def setsticky(self, widget, **kargs):
		result = self.wTree.get_widget("StickyChkBtn").get_active() == True
		self.client.set_bool(self.gconf_dir+'/sticky',result)

	def maxNumberOfResults(self, widget, **kargs):
		self.client.set_int(self.gconf_dir+'/max_number_of_search_result',int(self.wTree.get_widget("MaximumNumberOfResultSpin1").get_value()))

